package geometry;

/**
 * Represents a point in three-dimensional space.
 *
 * @author Catastrophe
 */
public final class Point3D
{
	/**
	 * This point's X coordinate.
	 */
	private double x;

	/**
	 * This point's Y coordinate.
	 */
	private double y;

	/**
	 * This point's Z coordinate.
	 */
	private double z;

	/**
	 * @return the Point3D representing the origin.
	 */
	public static Point3D origin()
	{
		return new Point3D(0.0, 0.0, 0.0);
	}

	/**
	 * Creates a new Point3D.
	 *
	 * @param x the new point's X coordinate.
	 * @param y the new point's Y coordinate.
	 * @param z the new point's Z coordinate.
	 */
	public Point3D(double x, double y, double z)
	{
		this.x = x;
		this.y = y;
		this.z = z;
	}

	/**
	 * @return this point's X coordinate.
	 */
	public double getX()
	{
		return x;
	}

	/**
	 * @return this point's Y coordinate.
	 */
	public double getY()
	{
		return y;
	}

	/**
	 * @return this point's Z coordinate.
	 */
	public double getZ()
	{
		return z;
	}

	/**
	 * Changes this point's X coordinate.
	 *
	 * @param x the new X coordinate.
	 */
	public void setX(double x)
	{
		this.x = x;
	}

	/**
	 * Changes this point's Y coordinate.
	 *
	 * @param y the new Y coordinate.
	 */
	public void setY(double y)
	{
		this.y = y;
	}

	/**
	 * Changes this point's Z coordinate.
	 *
	 * @param z the new Z coordinate.
	 */
	public void setZ(double z)
	{
		this.z = z;
	}

	/**
	 * Inverts this point across all axes.
	 */
	public void invert()
	{
		setX(-getX());
		setY(-getY());
		setZ(-getZ());
	}

	/**
	 * @param point the point to find distance to.
	 * @return the straight-line distance between this point and the specified point.
	 * @throws NullPointerException if the specified point is <code>null</code>.
	 */
	public double distanceTo(Point3D point) throws NullPointerException
	{
		return Math.pow(Math.pow(getX() - point.getX(), 2.0) + Math.pow(getY() - point.getY(), 2.0) + Math.pow(getZ() - point.getZ(), 2.0), 0.5);
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#clone()
	 */
	@Override
	public Point3D clone()
	{
		return new Point3D(getX(), getY(), getZ());
	}

	/**
	 * @param other the Point3D to check for equivalence to this Point3D.
	 * @return <code>true</code> if both points are equivalent or <code>false</code> otherwise.
	 */
	public boolean equals(Point3D other)
	{
		return other == null ? false : other.getX() == getX() && other.getY() == getY() && other.getZ() == getZ();
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object other)
	{
		return other instanceof Point3D ? equals((Point3D)other) : false;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode()
	{
		return new Double(getX()).hashCode() * 3 + new Double(getY()).hashCode() * 5 + new Double(getZ()).hashCode() * 7 + 13;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return "[" + getX() + ", " + getY() + ", " + getZ() + "]";
	}
}